from nose.tools import eq_
from cStringIO import StringIO

from serve import HTTPServer, HTTPRequest

class MockConnection(object):
    """Provide dummy socket methods with StringIO"""

    def __init__(self, data=''):
        self._sent = StringIO()
        self._recv = StringIO(data)

    def send(self, data):
        self._sent.write(data)

    def recv(self, buf_size):
        return self._recv.read(buf_size)

    def close(self):
        pass

    def get_sent(self):
        self._sent.seek(0)
        return self._sent.readlines()

class MockClient(object):
    """Skip listen loop and jack into server handler stuff directly"""

    def __init__(self, server):
        self.server = server

    def __call__(self, URL, method="GET", body=None, **headers):
        headers.setdefault('host', '127.0.0.1')
        headers.setdefault('user_agent', 'MockClient')

        data = ['%s %s HTTP/1.0' % (method, URL)]
        for key, value in headers.iteritems():
            key = '-'.join(part.title() for part in key.split('_'))
            data.append('%s: %s' % (key, value))

        data.append('')

        if body:
            data.append(body)

        conn = MockConnection('\r\n'.join(data))
        self.server.on_connect(conn, None)

        response = conn.get_sent()
        reply = response[0].strip().split(' ', 2)

        headers = {}
        for pos, line in enumerate(response[1:]):
            if line == '\r\n':
                break
            key, value = line.split(': ', 1)
            headers[key.upper()] = value

        body = '\r\n'.join(response[pos+2:])
        return reply, headers, body

class TestRequest(object):
    def test(self):
        conn = MockConnection()
        req = HTTPRequest(conn, 'GET', '/', {})
        req.response('itw orks!')

        data = conn.get_sent()

        eq_('HTTP/1.0 200 OK\r\n', data.pop(0))
        eq_('itw orks!', "".join(data[data.index('\r\n')+1:]))


class TestServer(object):
    def setup(self):
        self.server = HTTPServer(18000)
        self.client = MockClient(self.server)

        self.server.register(lambda r: r.url == '/',
                             lambda r: r.response('itw orks!'))

    def test_basic(self):
        reply, headers, body = self.client('/')

        eq_('200', reply[1])
        eq_('OK', reply[2])
        eq_('itw orks!', body)
       
    def test_handlers(self):
        self.server.register(lambda r: r.method == 'POST',
                             lambda r: r.response('sure, whatever'))
        self.server.register(lambda r: r.url.startswith('/lol/'),
                             lambda r: r.response('ok'))

        eq_('ok', self.client('/lol/')[-1])
        eq_('sure, whatever', self.client('/lol/', method='POST')[-1])
        eq_('404', self.client('/not/really/')[0][1])
        
if __name__ == "__main__":
    import nose
    nose.run()
